package com.kexio.auth.web;

import java.nio.file.AccessDeniedException;
import java.sql.SQLException;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.dao.DataAccessException;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.validation.BindException;
import org.springframework.validation.FieldError;
import org.springframework.web.HttpMediaTypeNotSupportedException;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.MissingServletRequestParameterException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException;
import org.springframework.web.multipart.MaxUploadSizeExceededException;
import org.springframework.web.servlet.HandlerExceptionResolver;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.NoHandlerFoundException;

import com.kexio.common.dto.Result;
import com.kexio.common.exception.BusinessException;

import cn.dev33.satoken.exception.NotLoginException;
import cn.dev33.satoken.exception.NotPermissionException;
import cn.dev33.satoken.exception.NotRoleException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;

/**
 * 全局异常处理器
 * 
 * 提供详细的异常信息，便于开发调试和问题定位
 *
 * @author Kexio Team
 * @since 2.0.0
 */
@RestControllerAdvice
public class GlobalExceptionHandler implements HandlerExceptionResolver {
    
    private static final Logger log = LoggerFactory.getLogger(GlobalExceptionHandler.class);
    
    @Value("${spring.profiles.active:dev}")
    private String activeProfile;
    
    /**
     * 是否为开发环境
     */
    private boolean isDev() {
        return "dev".equals(activeProfile) || "local".equals(activeProfile);
    }
    
    /**
     * 处理业务异常
     */
    @ExceptionHandler(BusinessException.class)
    public Result<Void> handleBusinessException(BusinessException e) {
        log.warn("业务异常: code={}, message={}", e.getErrorCode(), e.getMessage());
        return Result.error(e.getMessage());
    }
    
    /**
     * 处理参数校验异常（非法参数）
     */
    @ExceptionHandler(IllegalArgumentException.class)
    public Result<Void> handleIllegalArgumentException(IllegalArgumentException e) {
        log.warn("参数异常: {}", e.getMessage());
        return Result.error(e.getMessage());
    }
    
    /**
     * 处理参数校验异常（状态非法）
     */
    @ExceptionHandler(IllegalStateException.class)
    public Result<Void> handleIllegalStateException(IllegalStateException e) {
        log.warn("状态异常: {}", e.getMessage());
        return Result.error(e.getMessage());
    }
    
    /**
     * 处理空指针异常
     */
    @ExceptionHandler(NullPointerException.class)
    public Result<Void> handleNullPointerException(NullPointerException e) {
        log.error("[异常] NullPointerException");
        logExceptionChain(e);
        
        String message = isDev() 
            ? "空指针异常: " + (e.getMessage() != null ? e.getMessage() : getBusinessStackTrace(e))
            : "数据处理异常，请稍后重试";
        return Result.error(message);
    }
    
    /**
     * 处理数组越界异常
     */
    @ExceptionHandler({IndexOutOfBoundsException.class, ArrayIndexOutOfBoundsException.class})
    public Result<Void> handleIndexOutOfBoundsException(RuntimeException e) {
        log.error("数组越界异常: {}", e.getMessage(), e);
        String message = isDev() 
            ? "数组越界: " + e.getMessage()
            : "数据访问异常，请稍后重试";
        return Result.error(message);
    }
    
    /**
     * 处理类型转换异常
     */
    @ExceptionHandler(ClassCastException.class)
    public Result<Void> handleClassCastException(ClassCastException e) {
        log.error("类型转换异常: {}", e.getMessage(), e);
        String message = isDev() 
            ? "类型转换异常: " + e.getMessage()
            : "数据类型错误，请稍后重试";
        return Result.error(message);
    }
    
    // ==================== Spring MVC 异常 ====================
    
    /**
     * 处理缺少请求参数异常
     */
    @ExceptionHandler(MissingServletRequestParameterException.class)
    public Result<Void> handleMissingServletRequestParameterException(MissingServletRequestParameterException e) {
        log.warn("缺少请求参数: {}", e.getMessage());
        return Result.error("缺少必需参数: " + e.getParameterName());
    }
    
    /**
     * 处理参数类型不匹配异常
     */
    @ExceptionHandler(MethodArgumentTypeMismatchException.class)
    public Result<Void> handleMethodArgumentTypeMismatchException(MethodArgumentTypeMismatchException e) {
        log.warn("参数类型不匹配: {}", e.getMessage());
        return Result.error("参数类型错误: " + e.getName() + " 应为 " + 
                           (e.getRequiredType() != null ? e.getRequiredType().getSimpleName() : "未知类型"));
    }
    
    /**
     * 处理请求体不可读异常
     */
    @ExceptionHandler(HttpMessageNotReadableException.class)
    public Result<Void> handleHttpMessageNotReadableException(HttpMessageNotReadableException e) {
        log.warn("请求体不可读: {}", e.getMessage());
        String message = "请求体格式错误";
        if (isDev() && e.getMessage() != null) {
            if (e.getMessage().contains("JSON")) {
                message = "JSON格式错误: " + extractJsonError(e.getMessage());
            } else {
                message = "请求体解析失败: " + e.getMessage();
            }
        }
        return Result.error(message);
    }
    
    /**
     * 处理不支持的媒体类型异常
     */
    @ExceptionHandler(HttpMediaTypeNotSupportedException.class)
    public Result<Void> handleHttpMediaTypeNotSupportedException(HttpMediaTypeNotSupportedException e) {
        log.warn("不支持的媒体类型: {}", e.getMessage());
        return Result.error("不支持的请求类型: " + e.getContentType() + 
                           "，支持的类型: " + e.getSupportedMediaTypes());
    }
    
    /**
     * 处理不支持的请求方法异常
     */
    @ExceptionHandler(HttpRequestMethodNotSupportedException.class)
    public Result<Void> handleHttpRequestMethodNotSupportedException(HttpRequestMethodNotSupportedException e) {
        log.warn("不支持的请求方法: {}", e.getMessage());
        return Result.error("不支持的请求方法: " + e.getMethod() + 
                           "，支持的方法: " + String.join(", ", e.getSupportedMethods()));
    }
    
    /**
     * 处理404异常（端点不存在）
     */
    @ExceptionHandler(NoHandlerFoundException.class)
    public Result<Void> handleNoHandlerFoundException(NoHandlerFoundException e) {
        log.warn("[404] {} {} 端点不存在", e.getHttpMethod(), e.getRequestURL());
        
        String message = isDev() 
            ? String.format("接口不存在: %s %s", e.getHttpMethod(), e.getRequestURL())
            : "请求的接口不存在";
        
        return Result.error(404, message);
    }
    
    /**
     * 处理参数校验异常（@Valid）
     */
    @ExceptionHandler(MethodArgumentNotValidException.class)
    public Result<Void> handleMethodArgumentNotValidException(MethodArgumentNotValidException e) {
        log.warn("参数校验失败: {}", e.getMessage());
        StringBuilder message = new StringBuilder("参数校验失败: ");
        for (FieldError error : e.getBindingResult().getFieldErrors()) {
            message.append(error.getField())
                   .append("(")
                   .append(error.getDefaultMessage())
                   .append("); ");
        }
        return Result.error(message.toString());
    }
    
    /**
     * 处理参数绑定异常
     */
    @ExceptionHandler(BindException.class)
    public Result<Void> handleBindException(BindException e) {
        log.warn("参数绑定失败: {}", e.getMessage());
        StringBuilder message = new StringBuilder("参数绑定失败: ");
        for (FieldError error : e.getFieldErrors()) {
            message.append(error.getField())
                   .append("(")
                   .append(error.getDefaultMessage())
                   .append("); ");
        }
        return Result.error(message.toString());
    }
    
    /**
     * 处理文件上传大小超限异常
     */
    @ExceptionHandler(MaxUploadSizeExceededException.class)
    public Result<Void> handleMaxUploadSizeExceededException(MaxUploadSizeExceededException e) {
        log.warn("文件上传大小超限: {}", e.getMessage());
        return Result.error("文件大小超过限制");
    }
    
    // ==================== 权限异常 ====================
    
    /**
     * 处理未登录异常
     * 返回HTTP 401状态码，前端会自动跳转到登录页
     */
    @ExceptionHandler(NotLoginException.class)
    public ResponseEntity<Result<Void>> handleNotLoginException(NotLoginException e) {
        log.warn("未登录异常: {}", e.getMessage());
        Result<Void> result = Result.unauthorized("认证失败，请重新登录");
        return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body(result);
    }
    
    /**
     * 处理无权限异常
     * 返回HTTP 403状态码
     */
    @ExceptionHandler(NotPermissionException.class)
    public ResponseEntity<Result<Void>> handleNotPermissionException(NotPermissionException e) {
        log.warn("无权限异常: {}", e.getMessage());
        Result<Void> result = Result.forbidden("权限不足，需要权限: " + e.getPermission());
        return ResponseEntity.status(HttpStatus.FORBIDDEN).body(result);
    }
    
    /**
     * 处理无角色异常
     * 返回HTTP 403状态码
     */
    @ExceptionHandler(NotRoleException.class)
    public ResponseEntity<Result<Void>> handleNotRoleException(NotRoleException e) {
        log.warn("无角色异常: {}", e.getMessage());
        Result<Void> result = Result.forbidden("权限不足，需要角色: " + e.getRole());
        return ResponseEntity.status(HttpStatus.FORBIDDEN).body(result);
    }
    
    /**
     * 处理访问拒绝异常
     * 返回HTTP 403状态码
     */
    @ExceptionHandler(AccessDeniedException.class)
    public ResponseEntity<Result<Void>> handleAccessDeniedException(AccessDeniedException e) {
        log.warn("访问拒绝: {}", e.getMessage());
        Result<Void> result = Result.forbidden("访问被拒绝: " + e.getMessage());
        return ResponseEntity.status(HttpStatus.FORBIDDEN).body(result);
    }
    
    // ==================== 数据库异常 ====================
    
    /**
     * 处理SQL异常
     */
    @ExceptionHandler(SQLException.class)
    public Result<Void> handleSQLException(SQLException e) {
        log.error("[异常] SQLException code={} state={}", e.getErrorCode(), e.getSQLState());
        logExceptionChain(e);
        
        String message = isDev() 
            ? "数据库异常: " + e.getMessage()
            : "数据处理异常，请稍后重试";
        return Result.error(message);
    }
    
    /**
     * 处理数据访问异常
     */
    @ExceptionHandler(DataAccessException.class)
    public Result<Void> handleDataAccessException(DataAccessException e) {
        log.error("[异常] DataAccessException");
        logExceptionChain(e);
        
        String message = isDev() 
            ? "数据访问异常: " + e.getMessage()
            : "数据处理异常，请稍后重试";
        return Result.error(message);
    }
    
    // ==================== 通用异常 ====================
    
    /**
     * 处理运行时异常
     */
    @ExceptionHandler(RuntimeException.class)
    public Result<Void> handleRuntimeException(RuntimeException e) {
        log.error("[异常] {}", e.getClass().getSimpleName());
        logExceptionChain(e);
        
        String message = isDev() 
            ? e.getClass().getSimpleName() + ": " + (e.getMessage() != null ? e.getMessage() : getBusinessStackTrace(e))
            : "系统运行异常，请稍后重试";
        return Result.error(message);
    }
    
    /**
     * 处理系统异常（兜底）
     */
    @ExceptionHandler(Exception.class)
    public Result<Void> handleSystemException(Exception e) {
        log.error("[异常] {}", e.getClass().getSimpleName());
        logExceptionChain(e);
        
        String message = isDev() 
            ? e.getClass().getSimpleName() + ": " + (e.getMessage() != null ? e.getMessage() : getBusinessStackTrace(e))
            : "系统内部错误，请稍后重试";
        return Result.error(message);
    }
    
    /**
     * 实现HandlerExceptionResolver接口
     */
    @Override
    public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, 
                                       Object handler, Exception ex) {
        return null;
    }
    
    // ==================== 辅助方法 ====================
    
    /**
     * 记录异常调用链（只记录业务代码）
     */
    private void logExceptionChain(Throwable e) {
        StackTraceElement[] stackTrace = e.getStackTrace();
        if (stackTrace == null || stackTrace.length == 0) {
            return;
        }
        
        // 提取核心错误信息
        String errorMessage = extractErrorMessage(e);
        log.error("  错误信息: {}", errorMessage);
        
        // 找到业务代码的调用链
        log.error("  业务调用链:");
        int count = 0;
        for (StackTraceElement element : stackTrace) {
            String className = element.getClassName();
            
            // 只记录我们的业务代码（Controller, Service, Mapper）
            if (className.startsWith("com.kexio")) {
                String simpleClassName = className.substring(className.lastIndexOf('.') + 1);
                String methodName = element.getMethodName();
                int lineNumber = element.getLineNumber();
                
                // 判断代码层级（对齐格式）
                String layer;
                if (className.contains(".controller.")) {
                    layer = "Controller";
                } else if (className.contains(".service.")) {
                    layer = "Service   ";
                } else if (className.contains(".mapper.")) {
                    layer = "Mapper    ";
                } else if (className.contains(".handler.")) {
                    layer = "Handler   ";
                } else {
                    layer = "Business  ";
                }
                
                log.error("    [{}] {}.{}() 第{}行", 
                    layer, simpleClassName, methodName, lineNumber);
                
                count++;
                if (count >= 10) {  // 最多显示10层调用
                    break;
                }
            }
        }
        
        if (count == 0) {
            log.error("    (未找到业务代码调用)");
        }
    }
    
    /**
     * 提取核心错误信息（去掉冗长的前缀）
     */
    private String extractErrorMessage(Throwable e) {
        String message = e.getMessage();
        if (message == null || message.isEmpty()) {
            return e.getClass().getSimpleName();
        }
        
        // 如果是数据库错误，提取核心错误信息
        if (message.contains("### Error querying database") || message.contains("### Error updating database")) {
            // 提取 Cause 信息
            int causeIndex = message.indexOf("### Cause:");
            if (causeIndex > 0) {
                String cause = message.substring(causeIndex + 10).trim();
                // 只取第一行
                int newlineIndex = cause.indexOf('\n');
                if (newlineIndex > 0) {
                    cause = cause.substring(0, newlineIndex);
                }
                return cause;
            }
            
            // 查找根本原因
            Throwable rootCause = getRootCause(e);
            if (rootCause != null && rootCause.getMessage() != null) {
                return rootCause.getMessage();
            }
        }
        
        // 如果消息太长，截取前 200 个字符
        if (message.length() > 200) {
            return message.substring(0, 200) + "...";
        }
        
        return message;
    }
    
    /**
     * 获取根本原因异常
     */
    private Throwable getRootCause(Throwable e) {
        Throwable cause = e.getCause();
        if (cause == null) {
            return e;
        }
        
        // 最多追溯10层
        for (int i = 0; i < 10 && cause.getCause() != null; i++) {
            cause = cause.getCause();
        }
        
        return cause;
    }
    
    /**
     * 获取业务代码的堆栈信息（用于返回给前端）
     */
    private String getBusinessStackTrace(Throwable e) {
        StackTraceElement[] stackTrace = e.getStackTrace();
        if (stackTrace != null && stackTrace.length > 0) {
            // 找到第一个业务代码
            for (StackTraceElement element : stackTrace) {
                if (element.getClassName().startsWith("com.kexio")) {
                    String className = element.getClassName();
                    String simpleClassName = className.substring(className.lastIndexOf('.') + 1);
                    return simpleClassName + "." + element.getMethodName() + ":" + element.getLineNumber();
                }
            }
            // 如果没找到业务代码，返回第一行
            StackTraceElement element = stackTrace[0];
            return element.getClassName() + "." + element.getMethodName() + ":" + element.getLineNumber();
        }
        return "未知位置";
    }
    
    /**
     * 获取异常堆栈第一行
     */
    private String getStackTraceFirstLine(Throwable e) {
        return getBusinessStackTrace(e);
    }
    
    /**
     * 提取JSON错误信息
     */
    private String extractJsonError(String message) {
        if (message.contains("Unexpected character")) {
            return "包含非法字符";
        } else if (message.contains("was expecting")) {
            return "格式不完整";
        } else if (message.contains("Cannot deserialize")) {
            return "数据类型不匹配";
        }
        return "格式错误";
    }
}